package com.seleniti.enpareja.web.appbase.client.ui.main;

import java.util.Objects;

import org.ioc.commons.impl.gwt.client.ui.DisplayComposite;
import org.ioc.commons.impl.gwt.client.ui.HasEnabledWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasPendingOperationsLoaderWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasTextWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasVisibilityWrapper;
import org.ioc.commons.ui.HasLoader;
import org.ioc.commons.ui.HasPendingOperationsLoader;
import org.ioc.commons.ui.HasText;
import org.ioc.commons.ui.HasValue;
import org.ioc.commons.ui.HasVisibility;
import org.ioc.commons.ui.impl.HasLoaderAdapter;
import org.ioc.commons.ui.impl.HasValueAdapter;
import org.ioc.commons.ui.impl.HasVisibilityAdapter;
import org.ioc.commons.ui.impl.OnChangeCommand;
import org.ioc.commons.utils.Format;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.Widget;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.ui.main.AppShell;
import com.seleniti.enpareja.controller.ui.main.AppShell.Action;
import com.seleniti.enpareja.controller.ui.main.AppShell.Event;
import com.seleniti.enpareja.controller.ui.main.AppShell.Operation;
import com.seleniti.enpareja.controller.ui.main.AppShell.Presenter;
import com.seleniti.enpareja.controller.ui.main.AppShellPresenter;
import com.seleniti.enpareja.controller.ui.widget.SliderPanel;
import com.seleniti.enpareja.web.appbase.client.ui.widget.SliderPanelWidget;
import com.seleniti.enpareja.web.desktop.client.ui.resources.CommonResources;
import com.seleniti.enpareja.web.desktop.client.ui.widget.InfoInlineWidget;
import com.seleniti.enpareja.web.desktop.client.ui.widget.LoadingBarWidget;

public class AppShellWidget extends DisplayComposite<Event, Operation, Action, Presenter> implements AppShell {

	private static TemplateUiBinder uiBinder = GWT.create(TemplateUiBinder.class);

	interface TemplateUiBinder extends UiBinder<Widget, AppShellWidget> {
	}

	public static interface Style extends CssResource {
		String mainwrapper();

		String container();

		// String loginInfo();
	}

	@UiField
	CommonResources common;

	@UiField
	SliderPanelWidget pnlMainContainer;

	@UiField
	Style style;

	@UiField
	Panel headerPanel;

	@UiField
	PushButton btnBack;

	@UiField
	Button btnDeleteNow;

	@UiField
	Button btnLoadBasics;

	@UiField
	PushButton btnRefresh;

	@UiField
	LoadingBarWidget loadingBar;

	@UiField
	DisclosurePanel profilePanel;

	@UiField
	Panel nonAssociatedPanel;

	@UiField
	Panel pnlFirstSteps;

	@UiField
	Button btnCoupleRequest;

	@UiField
	PushButton btnSingleUser;

	@UiField
	PushButton btnTryMe;

	@UiField
	Button logout;

	@UiField
	Button btnAdmin;

	@UiField
	Button btnGettingStarted;

	@UiField
	Panel pnlGettingStarted;

	@UiField
	Panel pendingRequestsToYouPanel;

	@UiField
	Panel pendingRequestsFromYouPanel;

	@UiField
	Panel rejectedRequestsFromYouPanel;

	@UiField
	Button btnSwitchNow;

	@UiField
	InfoInlineWidget select2Info;

	private HandlerRegistration nativePreviewHandlerRegistration;

	private HandlerRegistration historyHandlerRegistration;

	private boolean previewAllNativeEvents = false;

	// @UiField
	// SimplePanel loadingBar;

	public AppShellWidget() {
		this.presenter = new AppShellPresenter(this, eventbus, operationManager, actionController);
		initWidget(uiBinder.createAndBindUi(this));

		headerPanel.getElement().getParentElement().getStyle().setOverflow(Overflow.VISIBLE);
	}

	@Override
	public SliderPanel mainContainer() {
		return pnlMainContainer;
	}

	@Override
	public void openLink(String url, boolean asExternal) {
		if (asExternal) {
			Window.open(url, "externalLink", "");
		} else {
			Window.Location.replace(url);
		}
	}

	@UiHandler("logout")
	void onLogoutButtonClicked(ClickEvent evt) {
		this.logout();
	}

	@UiHandler("btnSettings")
	void onSettingsButtonClicked(ClickEvent evt) {
		this.openSettings();
	}

	@Override
	public void openSettings() {
		profilePanel.setOpen(false);
		this.presenter.onOpenUserSettings();
	}

	@UiHandler("btnAdmin")
	void onAdminButtonClicked(ClickEvent evt) {
		this.openAdminConsole();
	}

	// @UiHandler("imgSeleniti")
	// void onSelenitiIconClicked(ClickEvent evt) {
	// this.openSelenitiWebSite();
	// }

	@Override
	public void openSelenitiWebSite() {
		this.presenter.onOpenSelenitiWebSite();
	}

	// @UiHandler("imgIocCommons")
	// void onIocCommonsIconClicked(ClickEvent evt) {
	// this.openIocCommonsWebSite();
	// }
	//
	// @Override
	// public void openIocCommonsWebSite() {
	// this.presenter.onOpenIocCommonsWebSite();
	// }

	@Override
	public void logout() {
		this.presenter.onLogout();
	}

	@Override
	public void openAdminConsole() {
		this.presenter.onOpenAdminConsole();
	}

	@Override
	public HasPendingOperationsLoader globalLoader() {
		return HasPendingOperationsLoaderWrapper.fromVisible(loadingBar, this);
	}

	@Override
	public HasVisibility goBackButtonVisible() {
		return HasVisibilityWrapper.from(btnBack, this);
	}

	@UiHandler("btnBack")
	void onBackButtonClicked(ClickEvent evt) {
		this.presenter.onGoBack();
	}

	@Override
	public HasValue<LoggedUserInfo> loggedUserInfo() {
		return HasValueAdapter.<LoggedUserInfo> forField("loggerUserInfo", this).onChange(
				new OnChangeCommand<LoggedUserInfo>() {

					@Override
					public void execute(LoggedUserInfo lui) {
						String headerText = lui != null && lui.getUserName() != null ? "► "
								+ Format.capitalize(lui.getUserName())
								+ (lui.getCoupleName() != null ? (lui.getCoupleType() == null
										|| lui.getCoupleType() == CoupleType.USERS_COUPLE ? " y "
										+ (Format.capitalize(lui.getCoupleName())) : "") : "") : "";
						profilePanel.setHeader(new Label(headerText));
						boolean loggedIn = lui != null && lui.isLogged();
						boolean admin = lui != null && lui.isLogged() && lui.isAdmin();
						boolean changed = !Objects.equals((Boolean) loggedIn, (Boolean) logout.isVisible());
						logout.setVisible(loggedIn);
						btnAdmin.setVisible(admin);

						if (changed && !loggedIn) {
							profilePanel.setOpen(false);
						}
					}
				});
	}

	@UiHandler("profilePanel")
	void onProfilePanelOpened(OpenEvent<DisclosurePanel> evt) {
		updateHandlers();
	}

	@UiHandler("profilePanel")
	void onProfilePanelClosed(CloseEvent<DisclosurePanel> evt) {
		updateHandlers();

	}

	private void updateHandlers() {
		// Remove any existing handlers.
		if (nativePreviewHandlerRegistration != null) {
			nativePreviewHandlerRegistration.removeHandler();
			nativePreviewHandlerRegistration = null;
		}
		if (historyHandlerRegistration != null) {
			historyHandlerRegistration.removeHandler();
			historyHandlerRegistration = null;
		}

		// Create handlers if showing.
		if (profilePanel.isOpen()) {
			nativePreviewHandlerRegistration = com.google.gwt.user.client.Event
					.addNativePreviewHandler(new NativePreviewHandler() {
						public void onPreviewNativeEvent(NativePreviewEvent event) {
							previewNativeEvent(event);
						}
					});
			historyHandlerRegistration = History.addValueChangeHandler(new ValueChangeHandler<String>() {
				public void onValueChange(ValueChangeEvent<String> event) {
					profilePanel.setOpen(false);
				}
			});
		}
	}

	@Override
	public HasVisibility nonAssociatedPanelVisibility() {
		return HasVisibilityWrapper.from(nonAssociatedPanel, this);
	}

	@Override
	public HasVisibility nonAssociatedButtonVisibility() {
		return HasVisibilityWrapper.from(btnCoupleRequest, this);
	}

	@Override
	public HasVisibility firstStepsVisibility() {
		return HasVisibilityWrapper.from(pnlFirstSteps, this);
	}

	@UiHandler("btnCoupleRequest")
	void onCoupleRequestButtonClicked(ClickEvent evt) {
		this.presenter.onGoToCoupleRequestsView();
	}

	@UiHandler("btnCoupleRequest2")
	void onCoupleRequest2ButtonClicked(ClickEvent evt) {
		this.presenter.onGoToCoupleRequestsView();
	}

	@UiHandler("btnGetCouple")
	void onGetCoupleButtonClicked(ClickEvent evt) {
		this.presenter.onGoToGetCoupleView();
	}

	@UiHandler("btnGetCouple2")
	void onGetCouple2ButtonClicked(ClickEvent evt) {
		this.presenter.onGoToGetCoupleView();
	}

	@UiHandler("btnGetCouple3")
	void onGetCouple3ButtonClicked(ClickEvent evt) {
		this.presenter.onGoToGetCoupleView();
	}

	@UiHandler("btnDeleteNow")
	void onDeleteNowButtonClicked(ClickEvent evt) {
		this.presenter.onDeleteNow();
	}

	@UiHandler("btnRefresh")
	void onRefreshNowButtonClicked(ClickEvent evt) {
		refreshNow();
	}

	@Override
	public void refreshNow() {
		this.presenter.onRefreshNow();
	}

	@Override
	public HasVisibility deleteButtonVisibility() {
		return HasVisibilityWrapper.from(btnDeleteNow);
	}

	@Override
	public HasVisibility refreshButtonVisibility() {
		return HasVisibilityWrapper.from(btnRefresh);
	}

	/**
	 * Preview the {@link NativePreviewEvent}.
	 *
	 * @param event
	 *            the {@link NativePreviewEvent}
	 */
	private void previewNativeEvent(NativePreviewEvent event) {
		// If the event has been canceled or consumed, ignore it
		if (event.isCanceled() || (!previewAllNativeEvents && event.isConsumed())) {
			// We need to ensure that we cancel the event even if its been
			// consumed so
			// that popups lower on the stack do not auto hide
			return;
		}

		// // Fire the event hook and return if the event is canceled
		// onPreviewNativeEvent(event);
		if (event.isCanceled()) {
			return;
		}

		// // If the event targets the popup or the partner, consume it
		com.google.gwt.user.client.Event nativeEvent = com.google.gwt.user.client.Event.as(event.getNativeEvent());
		boolean eventTargetsPopupOrPartner = eventTargetsPopup(nativeEvent)/*
																			 * ||
																			 * eventTargetsPartner
																			 * (
																			 * nativeEvent
																			 * )
																			 */;
		if (eventTargetsPopupOrPartner) {
			event.consume();
		}

		// Switch on the event type
		int type = nativeEvent.getTypeInt();
		switch (type) {
		// case Event.ONKEYDOWN: {
		// if (!onKeyDownPreview((char) nativeEvent.getKeyCode(),
		// KeyboardListenerCollection.getKeyboardModifiers(nativeEvent))) {
		// event.cancel();
		// }
		// return;
		// }
		// case Event.ONKEYUP: {
		// if (!onKeyUpPreview((char) nativeEvent.getKeyCode(),
		// KeyboardListenerCollection.getKeyboardModifiers(nativeEvent))) {
		// event.cancel();
		// }
		// return;
		// }
		// case Event.ONKEYPRESS: {
		// if (!onKeyPressPreview((char) nativeEvent.getKeyCode(),
		// KeyboardListenerCollection.getKeyboardModifiers(nativeEvent))) {
		// event.cancel();
		// }
		// return;
		// }

		case com.google.gwt.user.client.Event.ONMOUSEDOWN:
		case com.google.gwt.user.client.Event.ONTOUCHSTART:
			// // Don't eat events if event capture is enabled, as this can
			// // interfere with dialog dragging, for example.
			// if (DOM.getCaptureElement() != null) {
			// event.consume();
			// return;
			// }

			if (!eventTargetsPopupOrPartner) {
				profilePanel.setOpen(false);
				return;
			}
			break;
		// case com.google.gwt.user.client.Event.ONMOUSEUP:
		// case com.google.gwt.user.client.Event.ONMOUSEMOVE:
		// case com.google.gwt.user.client.Event.ONCLICK:
		// case com.google.gwt.user.client.Event.ONDBLCLICK:
		// case com.google.gwt.user.client.Event.ONTOUCHEND: {
		// // Don't eat events if event capture is enabled, as this can
		// // interfere with dialog dragging, for example.
		// if (DOM.getCaptureElement() != null) {
		// event.consume();
		// return;
		// }
		// break;
		// }

		}
	}

	// protected void onPreviewNativeEvent(NativePreviewEvent event) {
	// // Cancel the event based on the deprecated onEventPreview() method
	// if (event.isFirstHandler() &&
	// !onEventPreview(com.google.gwt.user.client.Event.as(event.getNativeEvent())))
	// {
	// event.cancel();
	// }
	// }
	//
	// private boolean eventTargetsPartner(NativeEvent event) {
	// if (autoHidePartners == null) {
	// return false;
	// }
	//
	// EventTarget target = event.getEventTarget();
	// if (Element.is(target)) {
	// for (Element elem : autoHidePartners) {
	// if (elem.isOrHasChild(Element.as(target))) {
	// return true;
	// }
	// }
	// }
	// return false;
	// }

	/**
	 * Does the event target this popup?
	 *
	 * @param event
	 *            the native event
	 * @return true if the event targets the popup
	 */
	private boolean eventTargetsPopup(NativeEvent event) {
		EventTarget target = event.getEventTarget();
		if (Element.is(target)) {
			return getElement().isOrHasChild(Element.as(target));
		}
		return false;
	}

	@UiHandler("btnGettingStarted")
	void onGettingStartedButton(ClickEvent evt) {
		this.presenter.onGoToGettingStartedView();
	}

	@Override
	public HasVisibility gettingStartedInfoVisible() {
		return HasVisibilityWrapper.from(pnlGettingStarted, this);
	}

	@Override
	public HasVisibility requestsToYouPanelVisibility() {
		return HasVisibilityWrapper.from(pendingRequestsToYouPanel, this);
	}

	@Override
	public HasVisibility pendingRequestsFromYouPanelVisibility() {
		return HasVisibilityWrapper.from(pendingRequestsFromYouPanel, this);
	}

	@Override
	public HasVisibility rejectedRequestsFromYouPanelVisibility() {
		return HasVisibilityWrapper.from(rejectedRequestsFromYouPanel);
	}

	@Override
	public HasVisibility switchButtonVisibility() {
		return HasVisibilityWrapper.from(btnSwitchNow);
	}

	@UiHandler("btnSwitchNow")
	void onSwitchNowButtonClicked(ClickEvent evt) {
		this.presenter.onSwitchElementsNow();
	}

	@Override
	public HasVisibility select2InfoVisibility() {
		return this.select2Info;
	}

	@Override
	public void goToMarketPlace() {
		this.openLink("https://play.google.com/store/apps/details?id=com.seleniti.enpareja.android", true);
	}

	@Override
	public HasVisibility retryButtonVisibility() {
		// TODO Create button
		return HasVisibilityAdapter.forField("retryButtonVisibility", this);
	}

	@UiHandler("btnSingleUser")
	void onSingleUserButtonClicked(ClickEvent evt) {
		this.makeSingle();
	}

	@UiHandler("btnTryMe")
	void onTryMeButtonClicked(ClickEvent evt) {
		this.makeTestCouple();
	}

	@UiHandler("btnLoadBasics")
	void onLoadBasicsClicked(ClickEvent evt) {
		presenter.onLoadBasics();
	}

	@Override
	public void makeSingle() {
		this.presenter.onMakeSingle();
	}

	@Override
	public void makeTestCouple() {
		this.presenter.onMakeTestCouple();
	}

	@Override
	public HasLoader makingCoupleIndicator() {
		return HasLoaderAdapter.forField("makingCoupleIndicator", this).onChange(new OnChangeCommand<Boolean>() {

			@Override
			public void execute(Boolean value) {
				HasEnabledWrapper.setEnabled(!value, btnCoupleRequest, btnSingleUser, btnTryMe);
			}
		});
	}

	@Override
	public HasVisibility loadBasicsButtonVisibility() {
		return HasVisibilityWrapper.from(btnLoadBasics, this);
	}

	@Override
	public HasText loadBasicsButtonText() {
		return HasTextWrapper.from(btnLoadBasics, this);
	}
}
